package com.adtech.adms.commons.file.service.impl;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.util.Date;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessResourceFailureException;

import com.adtech.adms.commons.file.dao.FileInfosDAO;
import com.adtech.adms.commons.file.pojo.FileData;
import com.adtech.adms.commons.file.pojo.FileGroup;
import com.adtech.adms.commons.file.pojo.FileInfo;
import com.adtech.adms.commons.file.service.FileInfosService;
import com.adtech.core.Settings;

/**
 * 
 * 
 * @author 李源
 * @since 2011-8-15
 * 
 * @Copyright (C) AD Tech Workshop All Right Reserved
 */
public class FileInfosServiceImpl implements FileInfosService {
	private static final Logger log = Logger.getLogger(FileInfosServiceImpl.class);
	private static AtomicSize CURRENT_SIZE = new AtomicSize(0);
	private static final ExecutorService executorService = Executors.newCachedThreadPool();
	private static final ConcurrentLinkedQueue<String> fileQueue = new ConcurrentLinkedQueue<String>();

	private FileInfosDAO fileinfosDAO;

	public FileInfosDAO getFileinfosDAO() {
		return fileinfosDAO;
	}

	public void setFileinfosDAO(FileInfosDAO fileinfosDAO) {
		this.fileinfosDAO = fileinfosDAO;
	}

	public void addFileInfo(final FileInfo fileInfo) {
		safeSave(fileInfo);
	}
	private void safeSave(final FileInfo fileInfo) {
		if(fileInfo.getRealPath()!=null) {
			try {
				fileQueue.add(fileInfo.getRealPath());
				FileGroup fileGroup = fileInfo.getGroup();
				FileInputStream input = new FileInputStream(fileInfo.getRealPath());
				final int size = input.available();
				input.close();
				final FileInfosServiceImpl service = this;
				Runnable task = new Runnable() {
					public void run() {
						try {
							while(CURRENT_SIZE.get()+size > Settings.FILE_BUFFER_SIZE || !fileInfo.getRealPath().equals(fileQueue.peek())) {
								Thread.currentThread().sleep(Settings.FILE_WAIT_INTERVAL);
							}
							CURRENT_SIZE.addAndGet(size);
							fileQueue.poll();
							fileInfo.setGroup(null);
							service.save(fileInfo);
							CURRENT_SIZE.getAndDecrement(size);
							fileInfo.getFileData().setData(null);
						} catch (NumberFormatException e) {
							log.error("FileInfosServiceImpl.addFileInfo(): IOException", e);
						} catch (InterruptedException e) {
							log.error("FileInfosServiceImpl.addFileInfo(): IOException", e);
						}
					}
				};
				Future future = null;
				try {
					future =  executorService.submit(task);
					future.get();
					if(fileGroup!=null) {
						fileInfo.setGroup(fileGroup);
					}
		        } catch (InterruptedException e) {
		        	log.error("FileInfosServiceImpl.addFileInfo(): InterruptedException", e);
		            Thread.currentThread().interrupt();
		            if(future!=null) {
		            	future.cancel(true);
		            }
		        } catch (ExecutionException e) {
		        	log.error("FileInfosServiceImpl.addFileInfo(): ExecutionException", e);
		        }
			} catch (IOException e) {
				log.error("FileInfosServiceImpl.addFileInfo(): IOException", e);
			}
		}
	}
	public void save(final FileInfo fileInfo) {
		String fileName = fileInfo.getRealName();
		if (fileName != null) {
			int lastDot = fileName.lastIndexOf('.');
			if (lastDot != -1) {
				String ext = fileName.substring(lastDot + 1);
				if (fileInfo.getExt() == null || fileInfo.getExt().equals("")) {
					fileInfo.setExt(ext);
				}
			}
		}

		try {
			FileInputStream input = new FileInputStream(fileInfo.getRealPath());
			try {
				fileInfo.setLength(input.available());
				fileInfo.setDateTime(new Date());
				FileData fileData = new FileData();
				// fileData.setData(Hibernate.createBlob(input));
				byte[] b = new byte[input.available()];
				input.read(b);
				input.close();
				fileData.setData(b);
				// fileData.setAttachment(fileInfo);
				fileInfo.setFileData(fileData);

			} catch (IOException e) {
				e.printStackTrace();
				throw new DataAccessResourceFailureException("error   occurs   when   save   file ", e);
			}
			fileinfosDAO.add(fileInfo);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public FileInfo get(Serializable id) {
		FileInfo fileInfo = fileinfosDAO.getById(FileInfo.class, id);
		FileData filedata = fileInfo.getFileData();
		fileinfosDAO.initialize(filedata);
		return fileInfo;
	}

	public void delete(Serializable id) {
		FileInfo fileInfo = fileinfosDAO.getById(FileInfo.class, id);

		if (fileInfo != null) {
			fileinfosDAO.delete(FileInfo.class, id);
			// fileinfosDAO.delete(FileData.class, id);
			
			//FIXME (Dormin men) remove this line to fix the bug for deleting enclosure of data sync. 
			//fileinfosDAO.flushSession();
		}
	}

	@Override
	public FileGroup getFileGroupById(Serializable id) {
		return fileinfosDAO.getById(FileGroup.class, id);
	}

	@Override
	public void addFileGroup(FileGroup group) {
		fileinfosDAO.add(group);
	}

	public Object getById(Class clz, Serializable id) {
		return fileinfosDAO.getById(clz, id);
	}

}
